package com.beemans.common.utils

import android.animation.Animator
import android.animation.ObjectAnimator
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Rect
import android.view.GestureDetector
import android.view.MotionEvent
import android.view.View
import android.view.ViewConfiguration
import androidx.core.view.GestureDetectorCompat
import androidx.recyclerview.widget.RecyclerView
import kotlin.math.abs

/**
 * RecyclerView 左滑选择帮助类
 *
 * @author tiamosu
 * @date 2020/12/4.
 */
class ItemSlideHelper(
    context: Context,
    private val callback: Callback
) : RecyclerView.OnItemTouchListener, GestureDetector.OnGestureListener {
    private var targetView: View? = null
    private var activePointerId = 0
    private val touchSlop: Int
    private val maxVelocity: Int
    private val minVelocity: Int
    private var lastX = 0
    private var lastY = 0
    private var isDragging = false
    private var expandAndCollapseAnim: Animator? = null

    private val gestureDetector by lazy { GestureDetectorCompat(context, this) }

    private val horizontalRange: Int
        get() {
            val viewHolder = callback.getChildViewHolder(targetView)
            return callback.getHorizontalRange(viewHolder)
        }

    private val isExpanded: Boolean get() = targetView?.scrollX == horizontalRange && horizontalRange > 0
    private val isCollapsed: Boolean get() = targetView?.scrollX ?: 0 <= 0

    companion object {
        const val DEFAULT_DURATION = 200
    }

    init {
        //手势用于处理fling
        ViewConfiguration.get(context).apply {
            touchSlop = scaledTouchSlop
            maxVelocity = scaledMaximumFlingVelocity
            minVelocity = scaledMinimumFlingVelocity
        }
    }

    override fun onInterceptTouchEvent(rv: RecyclerView, e: MotionEvent): Boolean {
        //如果RecyclerView滚动状态不是空闲targetView不是空
        if (rv.scrollState != RecyclerView.SCROLL_STATE_IDLE) {
            if (targetView != null) {
                //隐藏已经打开
                smoothHorizontalExpandOrCollapse(DEFAULT_DURATION / 2.toFloat())
                targetView = null
            }
            return false
        }

        //如果正在运行动画 ，直接拦截什么都不做
        if (expandAndCollapseAnim?.isRunning == true) {
            return true
        }
        var needIntercept = false
        val x = e.x.toInt()
        val y = e.y.toInt()
        when (e.action) {
            MotionEvent.ACTION_DOWN -> {
                activePointerId = e.getPointerId(0)
                lastX = e.x.toInt()
                lastY = e.y.toInt()

                /*
                 * 如果之前有一个已经打开的项目，当此次点击事件没有发生在右侧的菜单中则返回TRUE，
                 * 如果点击的是右侧菜单那么返回FALSE这样做的原因是因为菜单需要响应Onclick
                 *
                 * */
                if (targetView != null) {
                    return !inView(x, y)
                }

                //查找需要显示菜单的view;
                targetView = callback.findTargetView(x.toFloat(), y.toFloat())
            }
            MotionEvent.ACTION_MOVE -> {
                val deltaX = x - lastX
                val deltaY = y - lastY
                if (abs(deltaY) > abs(deltaX)) {
                    return false
                }
                run {
                    isDragging = targetView != null && abs(deltaX) >= touchSlop
                    needIntercept = isDragging
                }
            }
            MotionEvent.ACTION_CANCEL, MotionEvent.ACTION_UP -> {
                /*
                 * 走这是因为没有发生过拦截事件
                 * */
                if (isExpanded) {
                    if (inView(x, y)) {
                        // 如果走这那行这个ACTION_UP的事件会发生在右侧的菜单中
                    } else {
                        //拦截事件,防止targetView执行onClick事件
                        needIntercept = true
                    }

                    //折叠菜单
                    smoothHorizontalExpandOrCollapse(DEFAULT_DURATION / 2.toFloat())
                }
                targetView = null
            }
        }
        // 此句代码是为了通知他的父ViewPager现在进行的是本控件的操作，不要对我的操作进行干扰
        rv.parent.requestDisallowInterceptTouchEvent(true)
        return needIntercept
    }

    override fun onTouchEvent(rv: RecyclerView, e: MotionEvent) {
        if (expandAndCollapseAnim?.isRunning == true || targetView == null) {
            return
        }

        //如果要响应fling事件设置将mIsDragging设为false
        if (gestureDetector.onTouchEvent(e)) {
            isDragging = false
            return
        }
        val x = e.x.toInt()
        when (e.action) {
            MotionEvent.ACTION_DOWN -> {
            }
            MotionEvent.ACTION_MOVE -> {
                val deltaX = (lastX - e.x).toInt()
                if (isDragging) {
                    horizontalDrag(deltaX)
                }
                lastX = x
            }
            MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> if (isDragging) {
                if (!smoothHorizontalExpandOrCollapse(0f) && isCollapsed) targetView = null
                isDragging = false
            }
        }
    }

    override fun onRequestDisallowInterceptTouchEvent(disallowIntercept: Boolean) {}

    override fun onDown(e: MotionEvent?) = false

    override fun onShowPress(e: MotionEvent?) {}

    override fun onSingleTapUp(e: MotionEvent?) = false

    override fun onScroll(
        e1: MotionEvent?,
        e2: MotionEvent?,
        distanceX: Float,
        distanceY: Float
    ) = false

    override fun onLongPress(e: MotionEvent?) {}

    override fun onFling(
        e1: MotionEvent?,
        e2: MotionEvent?,
        velocityX: Float,
        velocityY: Float
    ): Boolean {
        if (abs(velocityX) > minVelocity && abs(velocityX) < maxVelocity) {
            if (!smoothHorizontalExpandOrCollapse(velocityX)) {
                if (isCollapsed) {
                    targetView = null
                }
                return true
            }
        }
        return false
    }

    /**
     * 根据当前scrollX的位置判断是展开还是折叠
     *
     * @param velocityX 如果不等于0那么这是一次fling事件,否则是一次ACTION_UP或者ACTION_CANCEL
     */
    @SuppressLint("ObjectAnimatorBinding")
    private fun smoothHorizontalExpandOrCollapse(velocityX: Float): Boolean {
        if (targetView == null) {
            return false
        }
        val scrollX = targetView!!.scrollX
        val scrollRange = horizontalRange
        if (expandAndCollapseAnim != null) {
            return false
        }
        var to = 0
        var duration = DEFAULT_DURATION
        if (velocityX == 0f) {
            //如果已经展一半，平滑展开
            if (scrollX > scrollRange / 2) {
                to = scrollRange
            }
        } else {
            to = if (velocityX > 0) 0 else scrollRange
            duration = ((1f - abs(velocityX) / maxVelocity) * DEFAULT_DURATION).toInt()
        }
        if (to == scrollX) {
            return false
        }
        expandAndCollapseAnim = ObjectAnimator.ofInt(targetView, "scrollX", to).apply {
            this.duration = duration.toLong()
            addListener(object : Animator.AnimatorListener {
                override fun onAnimationStart(animation: Animator) {}
                override fun onAnimationEnd(animation: Animator) {
                    expandAndCollapseAnim = null
                    if (isCollapsed) {
                        targetView = null
                    }
                }

                override fun onAnimationCancel(animation: Animator) {
                    expandAndCollapseAnim = null
                }

                override fun onAnimationRepeat(animation: Animator) {}
            })
            start()
        }
        return true
    }

    /*
     * 根据targetView的scrollX计算出targetView的偏移，这样能够知道这个point是在右侧的菜单中
     * */
    private fun inView(x: Int, y: Int): Boolean {
        targetView?.apply {
            val scrollX = scrollX
            val left = width - scrollX
            val top = top
            val right = left + horizontalRange
            val bottom = bottom
            val rect = Rect(left, top, right, bottom)
            return rect.contains(x, y)
        }
        return false
    }

    /**
     * 根据touch事件来滚动View的scrollX
     *
     * @param delta
     */
    private fun horizontalDrag(delta: Int) {
        targetView?.apply {
            var scrollX = scrollX
            val scrollY = scrollY
            if (scrollX + delta <= 0) {
                scrollTo(0, scrollY)
                return
            }
            val horRange = horizontalRange
            scrollX += delta
            if (abs(scrollX) < horRange) {
                scrollTo(scrollX, scrollY)
            } else {
                scrollTo(horRange, scrollY)
            }
        }
    }

    /**
     * 左滑菜单Callback
     */
    interface Callback {
        fun getHorizontalRange(holder: RecyclerView.ViewHolder?): Int
        fun getChildViewHolder(childView: View?): RecyclerView.ViewHolder
        fun findTargetView(x: Float, y: Float): View?
    }
}